#pragma once
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include <unordered_map>

#include "Log.hpp"

typedef enum
{
    UNLOGIN,
    LOGIN
} ss_status;
typedef websocketpp::server<websocketpp::config::asio>::timer_ptr timer_ptr;

class Session
{
public:
    Session(int ssid) : _ssid(ssid), _status(UNLOGIN)
    {
        LOG(INFO, "Session: %p has be Build\n", this);
    }
    ~Session()
    {
        LOG(INFO, "Session: %p has be Destroyed\n", this);
    }

    void set_user(int uid) { _uid = uid; }
    int get_user() { return _uid; }
    bool is_login() { return _status == LOGIN; }
    void set_timer(timer_ptr tp) { _tp = tp; }
    void set_status(ss_status status) { _status = status; }
    timer_ptr &get_timer() { return _tp; }

    int GetSsid() { return _ssid; }

private:
    int _ssid;
    int _uid;
    ss_status _status;
    timer_ptr _tp;
};

const int SESSION_TIMEOUT = 30000;
const int SESSION_FOREVER = -1;
typedef std::shared_ptr<Session> session_ptr;

class SessionManage
{
    typedef websocketpp::server<websocketpp::config::asio> wsserver;

public:
    SessionManage(wsserver *psvr)
        : _next_ssid(1), _psvr(psvr)
    {
    }
    ~SessionManage()
    {
    }

    session_ptr create_session(int uid, ss_status status)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        session_ptr sp(new Session(_next_ssid));
        _session.insert(std::make_pair(_next_ssid, sp));
        sp->set_user(uid);
        sp->set_status(status);
        ++_next_ssid;

        return sp;
    }
    session_ptr get_session_by_ssid(int ssid)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        auto it = _session.find(ssid);
        if (it == _session.end())
        {
            return session_ptr();
        }
        return it->second;
    }
    void remove_session(int ssid)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        _session.erase(ssid);
    }

    void add_session(int ssid, session_ptr sp)
    {
        std::unique_lock<std::mutex> lock(_mtx);

        _session.insert(std::make_pair(ssid, sp));
    }

    // ms 为 -1 代表永久
    void set_session_expire_time(int ssid, int ms)
    {
        session_ptr sp = get_session_by_ssid(ssid);
        if (sp.get() == nullptr)
            return;

        timer_ptr tp = sp->get_timer();
        if (tp.get() == nullptr && ms == SESSION_FOREVER)
        {
            return;
        }
        else if (tp.get() == nullptr && ms != SESSION_FOREVER)
        {
            timer_ptr temp_tp = _psvr->set_timer(ms, std::bind(&SessionManage::remove_session, this, ssid));
            sp->set_timer(temp_tp);
        }
        else if (tp.get() != nullptr && ms == SESSION_FOREVER)
        {
            tp->cancel();

            sp->set_timer(timer_ptr());
            _psvr->set_timer(0, std::bind(&SessionManage::add_session, this, ssid, sp));
        }
        else if (tp.get() != nullptr && ms != SESSION_FOREVER)
        {
            LOG(INFO, "set_session_expire_time, ssid = %d\n", ssid);

            // 4. 在session设置了定时删除的情况下，将session重置删除时间。
            tp->cancel();//因为这个取消定时任务并不是立即取消的
            sp->set_timer(timer_ptr());
            _psvr->set_timer(0, std::bind(&SessionManage::add_session, this, sp->GetSsid(), sp));

            //重新给session添加定时销毁任务
            timer_ptr tmp_tp  = _psvr->set_timer(ms, 
                std::bind(&SessionManage::remove_session, this, sp->GetSsid()));
            //重新设置session关联的定时器
            sp->set_timer(tmp_tp);
        }
    }

private:
    int _next_ssid;
    std::mutex _mtx;
    std::unordered_map<int, session_ptr> _session; // ssid -> session
    wsserver *_psvr;
};
